/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.logging.security; import java.io.FilePermission; import java.security.Permission; import java.security.Permissions; /** * Custom implementation of a security manager, so we can control the * security environment for tests in this package. */ public class MockSecurityManager extends SecurityManager { private final Permissions permissions = new Permissions(); private static final Permission setSecurityManagerPerm = new RuntimePermission("setSecurityManager"); private int untrustedCodeCount = 0; public MockSecurityManager() { permissions.add(setSecurityManagerPerm); } /** * Define the set of permissions to be granted to classes in the o.a.c.l package, * but NOT to unit-test classes in o.a.c.l.security package. */ public void addPermission(Permission p) { permissions.add(p); } /** * This returns the number of times that a check of a permission failed * due to stack-walking tracing up into untrusted code. Any non-zero * value indicates a bug in JCL, ie a situation where code was not * correctly wrapped in an AccessController block. The result of such a * bug is that signing JCL is not sufficient to allow JCL to perform * the operation; the caller would need to be signed too. */ public int getUntrustedCodeCount() { return untrustedCodeCount; } public void checkPermission(Permission p) throws SecurityException { if (setSecurityManagerPerm.implies(p)) { // ok, allow this; we don't want to block any calls to setSecurityManager // otherwise this custom security manager cannot be reset to the original. // System.out.println("setSecurityManager: granted"); return; } // Allow read-only access to files, as this is needed to load classes! // Ideally, we would limit this to just .class and .jar files. if (p instanceof FilePermission) { FilePermission fp = (FilePermission) p; if (fp.getActions().equals("read")) { // System.out.println("Permit read of files"); return; } } System.out.println("\n\ntesting permission:" + p.getClass() + ":"+ p); Exception e = new Exception(); e.fillInStackTrace(); StackTraceElement[] stack = e.getStackTrace(); // scan the call stack from most recent to oldest. // start at 1 to skip the entry in the stack for this method for(int i=1; i<stack.length; ++i) { String cname = stack[i].getClassName(); System.out.println("" + i + ":" + stack[i].getClassName() + "." + stack[i].getMethodName() + ":" + stack[i].getLineNumber()); if (cname.equals("java.util.logging.Handler") && stack[i].getMethodName().equals("setLevel")) { // LOGGING CODE CAUSES ACCESSCONTROLEXCEPTION // http://www-01.ibm.com/support/docview.wss?uid=swg1IZ51152 return; } if (cname.equals("java.util.logging.Level") && stack[i].getMethodName().equals("getLocalizedLevelName")) { // LOGGING-156: OpenJDK 1.7 JULI code (java.util.logging.Level#getLocalizedLevelName) // calls ResourceBundle#getBundle() without using AccessController#doPrivileged() // requiring RuntimePermission: "accessClassInPackage.sun.util.logging.resources" return; } if (cname.equals("java.security.AccessController")) { // Presumably method name equals "doPrivileged" // // The previous iteration of this loop verified that the // PrivilegedAction.run method associated with this // doPrivileged method call had the right permissions, // so we just return here. Effectively, the method invoking // doPrivileged asserted that it checked the input params // and found them safe, and that code is trusted, so we // don't need to check the trust level of code higher in // the call stack. System.out.println("Access controller found: returning"); return; } else if (cname.startsWith("java.") || cname.startsWith("javax.") || cname.startsWith("junit.") || cname.startsWith("org.apache.tools.ant.") || cname.startsWith("sun.")) { // Code in these packages is trusted if the caller is trusted. // // TODO: maybe check class is loaded via system loader or similar rather // than checking name? Trusted domains may be different in alternative // jvms.. } else if (cname.startsWith("org.apache.commons.logging.security")) { // this is the unit test code; treat this like an untrusted client // app that is using JCL ++untrustedCodeCount; System.out.println("Untrusted code [testcase] found"); throw new SecurityException("Untrusted code [testcase] found"); } else if (cname.startsWith("org.apache.commons.logging.")) { if (permissions.implies(p)) { // Code here is trusted if the caller is trusted System.out.println("Permission in allowed set for JCL class"); } else { System.out.println("Permission refused:" + p.getClass() + ":" + p); throw new SecurityException("Permission refused:" + p.getClass() + ":" + p); } } else { // we found some code that is not trusted to perform this operation. System.out.println("Unexpected code: permission refused:" + p.getClass() + ":" + p); throw new SecurityException("Unexpected code: permission refused:" + p.getClass() + ":" + p); } } } }